

package com.caltco.cargo.ops.common.xss;

import com.alibaba.excel.util.StringUtils;
import com.caltco.cargo.ops.common.exception.RRException;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;


import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * XSS过滤
 * @author
 * @email
 * @date 2017-04-01 10:20
 */
@Slf4j
public class XssFilter implements Filter {
	/**
	 * 排除部分URL不做过滤
	 */
	private List<String> excludeUrls = new ArrayList<String>();

	/**
	 * 公告新增、修改用到富文本，对标签进行转义
	 */
	private List<String> noticeUrls = new ArrayList<String>();

	@Override
	public void init(FilterConfig config) throws ServletException {
	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
		HttpServletResponse response1 = (HttpServletResponse) response;
		HttpServletRequest req = null;
		if(request instanceof HttpServletRequest) {
			req = new RequestWrapper((HttpServletRequest) request);
			// 获取@RequestBody注解参数和post请求参数
			String body = ((RequestWrapper) req).getBody();
			//log.debug("过滤器输出body：{}" , body);

			String pathInfo = req.getPathInfo() == null ? "" : req.getPathInfo();
			String url = req.getServletPath() + pathInfo;
			String uri = req.getRequestURI();
			boolean isNoticeUrl = false;
			// 排除部分URL不做过滤。
			for (String str : excludeUrls) {
				if (uri.contains(str)) {
					chain.doFilter(req, response1);
					return;
				}
			}
			for (String st : noticeUrls) {
				if (uri.contains(st)) {
					isNoticeUrl = true;
					break;
				}
			}
			List<String> ll=getvalue(body == null ? "" : body);
			// 获取请求所有参数值，校验防止SQL注入，防止XSS漏洞
			if(ll!=null){
				for(String ss:ll) {
					// 校验是否存在SQL注入信息
					if (checkSQLInject(ss, url)) {
						errorResponse(response1,req);
						return;
					}
				}}

			//过滤地址栏里的参数
			Enumeration<?> params = req.getParameterNames();
			String paramN = null;
			while (params.hasMoreElements()) {
				paramN = (String) params.nextElement();
				String paramVale = req.getParameter(paramN);

				// 校验是否存在SQL注入信息
				if (checkSQLInject(paramVale, url)) {
					errorResponse(response1,req);
					return;
				}
			}
		}
		if(req == null) {
			chain.doFilter(request, response1);
		} else {
			chain.doFilter(req, response1);
		}
		/*XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
				(HttpServletRequest) request);
		chain.doFilter(xssRequest, response);*/
	}

	public  List<String> getvalue(String str){
		int len=str.length();
		if(len>5){
			List<String> l=new ArrayList<String>();
			for(int i=0;i<len;i++){
				if(str.charAt(i)==':'){
					i++;
					if(str.charAt(i)=='"'){
						int ii=str.indexOf('"', i+1);
						l.add(str.substring(i+1, ii));
						i=ii;
					}
				}
			}
			return l;
		}
		else return null;
	}

	private void errorResponse(HttpServletResponse response,HttpServletRequest request) throws IOException {
		String warning = "输入项中不能包含非法字符。";

        throw new RRException(warning, 506);
		//response.setContentType("text/html; charset=UTF-8");
		/*PrintWriter out = response.getWriter();

		out.println("{\"httpCode\":\"-9998\",\"msg\":\"" + warning + "\", \"输入值\": \"" +paramvalue + "\"}");
		out.flush();
		out.close();*/
	}

	public static boolean checkSQLInject(String str, String url) {
		if (StringUtils.isEmpty(str)) {
			return false;// 如果传入空串则认为不存在非法字符
		}

		// 判断黑名单
		String[] inj_stra = { "script", "mid", "master", "truncate", "insert", "select", "delete", "update", "declare",
				"iframe", "'", "onreadystatechange", "alert", "atestu", "xss", ";", "'", "\"", "<", ">", "(", ")", ",",
				"\\", "svg", "confirm", "prompt", "onload", "onmouseover", "onfocus", "onerror" };

		str = str.toLowerCase(); // sql不区分大小写

		for (String s : inj_stra) {
			if (str.contains(s)) {
				log.warn("xss防攻击拦截url:{}，原因：特殊字符，传入str={},包含特殊字符：{}", url, str, s);
				return true;
			}
		}
		return false;
	}

	@Override
	public void destroy() {
	}

}